/**
* Copyright (c) By zengqh.
*
* This program is just for fun or demo, in the hope that it  
* will be useful, you can redistribute it and/or modify freely.
*
* Time: 2013/03/30
* File: ForDiffusePass.cpp
* Blog: http://www.cnblogs.com/zengqh/
**/

#include "ForDiffusePass.h"
#include "render_system/ProgramGen.h"

namespace HY
{
ForDiffusePass::ForDiffusePass(RenderSystem* render_sys)
	: SceneRenderPass(render_sys)
	, pro_gen_(0)
{

}

ForDiffusePass::~ForDiffusePass()
{

}

void ForDiffusePass::render(const RenderableList& render_list)
{
	const std::string& diff_vs_str;
	const std::string& diff_ps_str;
	if (!pro_gen_)
	{
		pro_gen_ = new ProgramGen(render_sys_, diff_vs_str, diff_ps_str);
	}

	HY_ASSERT(pro_gen_);

	RenderableList::const_iterator iter = render_list.begin();
	for (; iter != render_list.end(); ++iter)
	{
		SceneNode* node = const_cast<SceneNode*>(*iter);

		Mesh* mesh = node->getMesh();

		if (mesh)
		{
			renderMesh(mesh);
		}
	}
}

void ForDiffusePass::renderMesh(Mesh* mesh, SceneNode* node)
{
	MeshLODLevel* lod_level0 = mesh->GetLODLevel(0);
	IndexBuffer* idx_buf = lod_level0->getIndexBuffer();
	const std::vector<Geometry>& geo_list = lod_level0->getGeometries();

	render_sys_->setIndexBuffer(idx_buf);

	if (!(getFlags() & ZWriteOnly))
	{
		HY_ADD_VS_FLAG(pro_flags_, VS_TEXCOORD0);
		HY_ADD_VS_FLAG(pro_flags_, FS_DIFFUSEMAP);
	}

	ProgramGen::GeneratedProgram* gen_pro = pro_gen_->getProgram(pro_flags_);

	HY_ASSERT(gen_pro);

	HY_ASSERT(render_sys_->setProgram(gen_pro->pro))

	GeneratedProgramUserData*& pGeneratedProgramUserData = static_cast<GeneratedProgramUserData*>(gen_pro->user_data);

	if (!pGeneratedProgramUserData)
	{
		pGeneratedProgramUserData = new GeneratedProgramUserData;
	}

	Program* pro = gen_pro->pro;
	MaterialBase* material = 0;

	static const std::string sDiffuseColor = "DiffuseColor";
	static const std::string sVertexPosition = "VertexPosition";
	static const std::string sVertexTexCoord0 = "VertexTexCoord0";
	static const std::string sObjectSpaceToClipSpaceMatrix = "ObjectSpaceToClipSpaceMatrix";
	static const std::string sAlphaReference = "AlphaReference";

	pGeneratedProgramUserData->v0 = pro->getAttribute(sVertexPosition);
	pGeneratedProgramUserData->t0 = pro->getAttribute(sVertexTexCoord0);
	pGeneratedProgramUserData->mvp = pro->getAttribute(sObjectSpaceToClipSpaceMatrix);
	pGeneratedProgramUserData->diff_clr = pro->getUniform(sDiffuseColor);
	pGeneratedProgramUserData->diff_map = pro->getUniform(MaterialBase::diffuse_map_);

	Parameter* param = 0;

	/** set diffuse clr. */
	if (!(getFlags() & ZWriteOnly))
	{
		if (pGeneratedProgramUserData->diff_clr)
		{
			param = material->getParameter(sDiffuseColor);
			if (param)
			{
				Vector3 diff_clr;
				param->getValue3fv(diff_clr.x_, diff_clr.y_, diff_clr.z_);
				pGeneratedProgramUserData->diff_clr->set3(diff_clr.Data());
			}
			else
			{
				Vector3 diff_clr(1.0f, 1.0f, 1.0f);
				pGeneratedProgramUserData->diff_clr->set3(diff_clr.Data());
			}
		}
	}

	/** diffuse map. */
	TextureBuffer* diff_map_buffer = 0;
	if (pGeneratedProgramUserData->diff_map)
	{
		const int nTextureUnit = pGeneratedProgramUserData->diff_map->set(diff_map_buffer);
		if (pGeneratedProgramUserData->alpha_ref)
		{
			param = material->getParameter(sAlphaReference);

			float alpha_ref = param->getValue1f();

			pGeneratedProgramUserData->alpha_ref->set(alpha_ref);

			if (alpha_ref != 0.0f)
			{
				HY_ADD_FS_FLAG(pro_flags_, FS_ALPHATEST)
			}
		}
	}

	/** mvp. */
	if (pGeneratedProgramUserData->mvp)
	{
		pGeneratedProgramUserData->mvp->set();
	}

	render_sys_->drawIndexedPrimitives();
}

}